#include "ShaderScript.hpp"
#include <Utility/StringTools.hpp>
#include <Utility/FileTools.hpp>
#include <Utility/Log.hpp>

namespace zzz{
bool Script<Shader>::LoadFromMemory(istringstream &iss, Shader &shader, const string &path)
{
  string FragmentShaderFilename="",VertexShaderFilename="";

  string buf;
  while(true)
  {
    iss>>buf;
    if (iss.fail()) break;
    ToLow(buf);
    if (buf=="v" || buf=="vert" || buf=="vertfile" || buf=="vertfilename")
    {
      iss>>buf;
      VertexShaderFilename=PathFile(path,buf);
    }
    else if (buf=="f" || buf=="frag" || buf=="fragfile" || buf=="fragfilename")
    {
      iss>>buf;
      FragmentShaderFilename=PathFile(path,buf);
    }
    else if (buf=="f1" || buf=="float1")
    {
      UniformParam tmp;
      tmp.type=UniformParam::F1;
      iss>>tmp.name;
      iss>>tmp.f1;
      shader.UniformParamList.push_back(tmp);
    }
    else if (buf=="f2" || buf=="float2")
    {
      UniformParam tmp;
      tmp.type=UniformParam::F2;
      iss>>tmp.name;
      iss>>tmp.f2[0]>>tmp.f2[1];
      shader.UniformParamList.push_back(tmp);
    }
    else if (buf=="f3" || buf=="float3")
    {
      UniformParam tmp;
      tmp.type=UniformParam::F3;
      iss>>tmp.name;
      iss>>tmp.f3[0]>>tmp.f3[1]>>tmp.f3[2];
      shader.UniformParamList.push_back(tmp);
    }
    else if (buf=="f4" || buf=="float4")
    {
      UniformParam tmp;
      tmp.type=UniformParam::F4;
      iss>>tmp.name;
      iss>>tmp.f4[0]>>tmp.f4[1]>>tmp.f4[2]>>tmp.f4[3];
      shader.UniformParamList.push_back(tmp);
    }
    else if (buf=="i1" || buf=="int1")
    {
      UniformParam tmp;
      tmp.type=UniformParam::I1;
      iss>>tmp.name;
      iss>>tmp.i1;
      shader.UniformParamList.push_back(tmp);
    }
    else if (buf=="i2" || buf=="int2")
    {
      UniformParam tmp;
      tmp.type=UniformParam::I2;
      iss>>tmp.name;
      iss>>tmp.i2[0]>>tmp.i2[1];
      shader.UniformParamList.push_back(tmp);
    }
    else if (buf=="i3" || buf=="int3")
    {
      UniformParam tmp;
      tmp.type=UniformParam::I3;
      iss>>tmp.name;
      iss>>tmp.i3[0]>>tmp.i3[1]>>tmp.i3[2];
      shader.UniformParamList.push_back(tmp);
    }
    else if (buf=="i4" || buf=="int4")
    {
      UniformParam tmp;
      tmp.type=UniformParam::I4;
      iss>>tmp.name;
      iss>>tmp.i4[0]>>tmp.i4[1]>>tmp.i4[2]>>tmp.i4[3];
      shader.UniformParamList.push_back(tmp);
    }
    else if (buf=="m2" || buf=="mat2" || buf=="matrix2" || buf=="mat2x2" || buf=="matrix2x2")
    {
      UniformParam tmp;
      tmp.type=UniformParam::M2;
      iss>>tmp.name;
      iss>>tmp.m2[0]>>tmp.m2[1]>>\
        tmp.m2[2]>>tmp.m2[3];
      shader.UniformParamList.push_back(tmp);
    }
    else if (buf=="m3" || buf=="mat3" || buf=="matrix3" || buf=="mat3x3" || buf=="matrix3x3")
    {
      UniformParam tmp;
      tmp.type=UniformParam::M3;
      iss>>tmp.name;
      iss>>tmp.m3[0]>>tmp.m3[1]>>tmp.m3[2]>>\
        tmp.m3[3]>>tmp.m3[4]>>tmp.m3[5]>>\
        tmp.m3[6]>>tmp.m3[7]>>tmp.m3[8];
      shader.UniformParamList.push_back(tmp);
    }
    else if (buf=="m4" || buf=="mat4" || buf=="matrix4" || buf=="mat4x4" || buf=="matrix4x4")
    {
      UniformParam tmp;
      tmp.type=UniformParam::M4;
      iss>>tmp.name;
      iss>>tmp.m4[0]>>tmp.m4[1]>>tmp.m4[2]>>tmp.m4[3]>>\
        tmp.m4[4]>>tmp.m4[5]>>tmp.m4[6]>>tmp.m4[7]>>\
        tmp.m4[8]>>tmp.m4[9]>>tmp.m4[10]>>tmp.m4[11]>>\
        tmp.m4[12]>>tmp.m4[13]>>tmp.m4[14]>>tmp.m4[15];
      shader.UniformParamList.push_back(tmp);
    }
    else if (buf=="objrot" || buf=="objrotate" || buf=="objrotmat" || buf=="objrotatematrix")
    {
      UniformParam tmp;
      tmp.type=UniformParam::OBJROT;
      iss>>tmp.name;
      shader.UniformParamList.push_back(tmp);
    }
    else if (buf=="objrottrans" || buf=="objrotatetranspose" || buf=="objrottransmat" || buf=="objrotatetransposematrix")
    {
      UniformParam tmp;
      tmp.type=UniformParam::OBJROTTRANS;
      iss>>tmp.name;
      shader.UniformParamList.push_back(tmp);
    }
    else if (buf=="envrot" || buf=="envrotate" || buf=="envrotmat" || buf=="envrotatematrix")
    {
      UniformParam tmp;
      tmp.type=UniformParam::ENVROT;
      iss>>tmp.name;
      shader.UniformParamList.push_back(tmp);
    }
    else if (buf=="envrottrans" || buf=="envrotatetranspose" || buf=="envrottransmat" || buf=="envrotatetransposematrix")
    {
      UniformParam tmp;
      tmp.type=UniformParam::ENVROTTRANS;
      iss>>tmp.name;
      shader.UniformParamList.push_back(tmp);
    }
    else
    {
      printf("UNKNOWN PARAMETER: %s\n",buf.c_str());
      return false;
    }
  }
  return ShaderScriptHelper::LoadShaderFile(VertexShaderFilename.c_str(),FragmentShaderFilename.c_str(),0,&shader);
}

bool ShaderScriptHelper::LoadShaderFile(const char * vertexFile, const char * fragmentFile,const char * geometryFile, Shader *o)
{
  zout<<"Load shader from file: ["<<vertexFile<<"],["<<fragmentFile<<"],["<<geometryFile<<"]...\n";
  bool ret=o->LoadVertexFile(vertexFile)\
    && o->LoadFragmentFile(fragmentFile)\
    && o->LoadGeometryFile(geometryFile)\
    && o->Link();
  return ret;
}

bool ShaderScriptHelper::LoadShaderMemory(const char * vertexMem, const char * fragmentMem, const char * geometryMem, Shader *o)
{
  zout<<"Load shader from memory...\n";
  bool ret=o->LoadVertexMemory(vertexMem)\
    && o->LoadFragmentMemory(fragmentMem)\
    && o->LoadGeometryMemory(geometryMem)\
    && o->Link();
  return ret;
}

Shader *ShaderScriptHelper::LoadShaderFile(const char * vertexFile, const char * fragmentFile,const char * geometryFile)
{
  zout<<"Load shader from file: ["<<vertexFile<<"],["<<fragmentFile<<"],["<<geometryFile<<"]...\n";
  Shader *o = new Shader;
  bool ret=o->LoadVertexFile(vertexFile)\
    && o->LoadFragmentFile(fragmentFile)\
    && o->LoadGeometryFile(geometryFile)\
    && o->Link();
  if (ret) return o;
  delete o;
  return NULL;
}

Shader *ShaderScriptHelper::LoadShaderMemory(const char * vertexMem, const char * fragmentMem, const char * geometryMem)
{
  zout<<"Load shader from memory...\n";
  Shader *o = new Shader;
  bool ret=o->LoadVertexMemory(vertexMem)\
    && o->LoadFragmentMemory(fragmentMem)\
    && o->LoadGeometryMemory(geometryMem)\
    && o->Link();
  if (ret) return o;
  delete o;
  return NULL;
}

}